/**
 * 
 */
package il.ac.bgu.cs.heuristics.ui;

import static il.ac.bgu.cs.heuristics.utils.MathUtils.average;
import il.ac.bgu.cs.heuristics.core.HeuristicEngineComplex;
import il.ac.bgu.cs.heuristics.core.HeuristicImpProxy;
import il.ac.bgu.cs.heuristics.core.IHeuristicsEngine;
import il.ac.bgu.cs.heuristics.core.caching.ICachingEngine;
import il.ac.bgu.cs.heuristics.core.caching.SimpleHashCaching;
import il.ac.bgu.cs.heuristics.graph.ActionGraph;
import il.ac.bgu.cs.heuristics.graph.GraphBuildEssentials;
import il.ac.bgu.cs.heuristics.graph.types.IState;
import il.ac.bgu.cs.heuristics.integration.BestFirstSearchAdapter;
import il.ac.bgu.cs.heuristics.integration.HeuristicValueAdaptingComparator;
import il.ac.bgu.cs.heuristics.utils.CollectionUtils;
import il.ac.bgu.cs.heuristics.utils.Pair;
import il.ac.bgu.cs.heuristics.utils.Tuple;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

import javaff.search.HValueComparator;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextArea;

import org.apache.log4j.Logger;

/**
 * @author Ronen Pinko
 * 
 */
@SuppressWarnings("serial")
public class MainWindow extends JFrame {
	private final static Logger			LOG							= Logger
																			.getLogger(MainWindow.class);

	/** the minimal distance between components */
	static final int					COMPONENT_MINIMUM_DISTANCE	= 10;
	/** how many pixels does the side borders take */
	static final int					WINDOW_WIDTH_OVERHEAD		= 20;
	/** how many pixels does the upper panel and border take */
	static final int					WINDOW_HEIGHT_OVERHEAD		= 60;
	private final static String			WINDOW_TITLE				= "Heuristics engine";
	private final static Pair<Integer>	WINDOW_DIMENSIONS			= new Pair<Integer>(
																			1024,
																			768);
	private final static int			OUTPUT_TEXT_AREA_POS_X		= 200;
	private final static int			OUTPUT_TEXT_AREA_POS_Y		= COMPONENT_MINIMUM_DISTANCE;
	private final static int			OUTPUT_TEXT_AREA_WIDTH		= WINDOW_DIMENSIONS
																			.getFirst()
																			- OUTPUT_TEXT_AREA_POS_X
																			- WINDOW_WIDTH_OVERHEAD
																			- COMPONENT_MINIMUM_DISTANCE;
	private final static int			OUTPUT_TEXT_AREA_HEIGHT		= WINDOW_DIMENSIONS
																			.getSecond()
																			- OUTPUT_TEXT_AREA_POS_Y
																			- WINDOW_HEIGHT_OVERHEAD;

	private final static int			PROBLEM_QUEUE_POS_X			= COMPONENT_MINIMUM_DISTANCE;
	private final static int			PROBLEM_QUEUE_POS_Y			= COMPONENT_MINIMUM_DISTANCE;
	private final static int			PROBLEM_QUEUE_WIDTH			= OUTPUT_TEXT_AREA_POS_X
																			- PROBLEM_QUEUE_POS_X
																			- COMPONENT_MINIMUM_DISTANCE;
	private final static int			PROBLEM_QUEUE_HEIGHT		= OUTPUT_TEXT_AREA_HEIGHT;

	private final Deque<Tuple<File>>	problemQueue				= new ArrayDeque<Tuple<File>>();
	private DefaultListModel			problemQueueModel;
	private JTextArea					outputTextArea;

	public MainWindow() {
		super();
		initBasicWindowProperties();
		createTextComponents();
		createMenus();
		addStaticProblems();

	}

	private Tuple<String> createStringTuple(String domainPath,
			String problemPath, String contradicationPath) {
		return new Tuple<String>(domainPath, problemPath, contradicationPath);

	}

	private void addStaticProblems() {

		List<Tuple<String>> list = CollectionUtils.newArrayList();
		final String domain1Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\blocksWorldTyped.pddl";
		final String problem1Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped1.pddl";
		final String problem2Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped2.pddl";
		final String problem3Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped3.pddl";
		final String problem4Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped4.pddl";
		final String problem5Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped5.pddl";
		final String problem6Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped6.pddl";
		final String problem7Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\problem1BlocksWorldTyped7.pddl";
		final String contradication1Path = "C:\\Documents and Settings\\haro\\Desktop\\outcome thesis\\blocksWorld\\c.txt";

		list.add(createStringTuple(domain1Path, problem1Path,
				contradication1Path));
		list.add(createStringTuple(domain1Path, problem2Path,
				contradication1Path));
		list.add(createStringTuple(domain1Path, problem3Path,
				contradication1Path));
		list.add(createStringTuple(domain1Path, problem4Path,
				contradication1Path));
		list.add(createStringTuple(domain1Path, problem5Path,
				contradication1Path));
		list.add(createStringTuple(domain1Path, problem6Path,
				contradication1Path));
		list.add(createStringTuple(domain1Path, problem7Path,
				contradication1Path));

		for (Tuple<String> problemStrings : list) {
			File domainFile = new File(problemStrings.get(0));
			File problemFile = new File(problemStrings.get(1));
			File ContradicationFile = new File(problemStrings.get(2));
			Tuple<File> problem = new Tuple<File>(domainFile, problemFile,
					ContradicationFile);

			problemQueue.add(problem);
			problemQueueModel.addElement(problem.get(0).getName() + ","
					+ problem.get(1).getName());
		}

	}

	private void initBasicWindowProperties() {
		final Integer winWidth = WINDOW_DIMENSIONS.getFirst();
		final Integer winHeight = WINDOW_DIMENSIONS.getSecond();
		setSize(winWidth, winHeight);
		setTitle(WINDOW_TITLE);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		final Dimension size = getToolkit().getScreenSize();
		setLocation(average(size.width, -winWidth), average(size.height,
				-winHeight));
	}

	private void createTextComponents() {
		final JPanel panel = new JPanel();
		panel.setLayout(null);
		outputTextArea = new JTextArea();
		outputTextArea.setLineWrap(true);
		outputTextArea.setWrapStyleWord(true);
		outputTextArea.setBounds(OUTPUT_TEXT_AREA_POS_X,
				OUTPUT_TEXT_AREA_POS_Y, OUTPUT_TEXT_AREA_WIDTH,
				OUTPUT_TEXT_AREA_HEIGHT);

		problemQueueModel = new DefaultListModel();
		final JList problemQueue = new JList(problemQueueModel);
		problemQueue.setBounds(PROBLEM_QUEUE_POS_X, PROBLEM_QUEUE_POS_Y,
				PROBLEM_QUEUE_WIDTH, PROBLEM_QUEUE_HEIGHT);

		panel.add(outputTextArea);
		panel.add(problemQueue);
		add(panel);
	}

	private void createMenus() {
		final JMenuBar menubar = new JMenuBar();
		final JMenu problemMenu = new JMenu("Problem");
		final JMenuItem addProblemItem = new JMenuItem("Add");
		addProblemItem.addActionListener(new AddProblemActionListener());

		final JMenuItem clearProblemItem = new JMenuItem("Clear");
		clearProblemItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				problemQueue.clear();
				problemQueueModel.clear();
				outputTextArea.setText("");
			}

		});

		final JMenuItem solveProblemItem = new JMenuItem("Solve!");
		solveProblemItem.addActionListener(new SolveActionListener());

		problemMenu.add(addProblemItem);
		problemMenu.add(solveProblemItem);
		problemMenu.add(clearProblemItem);
		menubar.add(problemMenu);
		setJMenuBar(menubar);
	}

	private final class SolveActionListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			Tuple<File> problem = problemQueue.poll();
			if (problem == null) {
				UiUtils.showErrorDialog("No problem to solve");
			}
			while (problem != null) {
				outputTextArea.append("\n***********new problem***********");
				problemQueueModel.remove(0);
				outputTextArea.append(nativeSolutionToString(problem));
				outputTextArea.append(ourSolutionToString(problem));
				problem = problemQueue.poll();
			}
		}

		private String ourSolutionToString(Tuple<File> problem) {
			final File domainFile = problem.get(0);
			final File problemFile = problem.get(1);
			try {
				final InputStream contradictions = new FileInputStream(problem
						.get(2));
				final GraphBuildEssentials graphEssentials = GraphBuildEssentials
						.createInstance(domainFile, problemFile, contradictions);
				final ActionGraph g = new ActionGraph(graphEssentials);
				final HeuristicEngineComplex heuristicEngine = new HeuristicEngineComplex(g);
				final ICachingEngine<Pair<IState>> cache = new SimpleHashCaching<Pair<IState>>();
				final IHeuristicsEngine proxy = new HeuristicImpProxy(
						heuristicEngine, cache);

				BestFirstSearchAdapter ourHeuristicSearchAdapter = new BestFirstSearchAdapter(
						new HeuristicValueAdaptingComparator(proxy, g
								.getGoalState(), graphEssentials));

				final String ourSolution = UiUtils.planToString(domainFile,
						problemFile, ourHeuristicSearchAdapter);

				return new StringBuilder().append("\n").append("Our:").append(
						"\n").append(ourSolution).append("\n").append(
						ourHeuristicSearchAdapter.getNodeCount()).append("\n")
						.append("=========================").append("\n")
						.toString();

				// final String ourSolution = UiUtils.planToString(domainFile,
				// problemFile, new BestFirstSearchAdapter(
				// new HeuristicValueAdaptingComparator(
				// heuristicEngine, g.getGoalState(),
				// graphEssentials)));
				// return new
				// StringBuilder().append("\n").append("Our:").append(
				// "\n").append(ourSolution).append("\n").append(
				// "=========================").append("\n").toString();
			} catch (FileNotFoundException e) {
				LOG.error(e);
			} catch (IOException e) {
				LOG.error(e);
			} catch (IllegalArgumentException e) {
				LOG.error(e);
			}
			return printProblemCantBeSolved(domainFile, problemFile);
		}

		private String printProblemCantBeSolved(final File domainFile,
				final File problemFile) {
			return ("problem <" + domainFile.getName() + ","
					+ problemFile.getName() + "> can't be solved, check logs");
		}

		private String nativeSolutionToString(Tuple<File> problem) {
			final File domainFile = problem.get(0);
			final File problemFile = problem.get(1);

			BestFirstSearchAdapter javaFFSearchAdapter = new BestFirstSearchAdapter(
					new HValueComparator());
			final String javaFFSolution = UiUtils.planToString(domainFile,
					problemFile, javaFFSearchAdapter);
			return new StringBuilder().append("\n").append("JavaFF:").append(
					"\n").append(javaFFSolution).append("\n").append(
					javaFFSearchAdapter.getNodeCount()).append("\n").append(
					"=========================").append("\n").toString();

			// final String originalSolution = UiUtils.planToString(domainFile,
			// problemFile, new BestFirstSearchAdapter(
			// new HValueComparator()));
			// return new StringBuilder().append(
			// "Solution for " + domainFile.getName() + ","
			// + problemFile.getName() + ":").append("\n").append(
			// "Original:").append("\n").append(originalSolution)
			// .toString();
		}
	}

	class AddProblemActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			final AddProblemDialog addDialog = new AddProblemDialog(this);
			addDialog.setVisible(true);
		}

		public void onProblemInputReceived(Tuple<File> problem) {
			problemQueue.add(problem);
			problemQueueModel.addElement(problem.get(0).getName() + ","
					+ problem.get(1).getName());
		}
	}

}
